﻿namespace Blaze.Editor.Vue
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;
    using EditorOnly;
    using Framework.LuaExtensions.FileLoaders;
    using UnityEditor;
    using UnityEngine;
    using XLua;
    using XLua.LuaDLL;

    public class AutoCompiler : IDisposable
    {
        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
        }

        #endregion

        public AutoCompiler()
        {
            EditorApplication.update += onEditorApplicationUpdate;
            mEnv.AddLoader(new FromDirectory(ProjectPath.Get("Lua")).ReadFile);
            mEnv.AddBuildin("rapidjson", Lua.LoadRapidJson);
        }

        public void Dispose(bool isDisposing)
        {
            if (mIsDisposed)
                return;
            mIsDisposed = true;
            if (mWatcher != null)
            {
                mWatcher.Dispose();
                mWatcher = null;
            }
            if (mEnv != null)
            {
                mEnv.Dispose(isDisposing);
                mEnv = null;
            }
        }

        public void Watch(string dir)
        {
            if (mWatcher != null)
            {
                mWatcher.Dispose();
                mWatcher = null;
            }
            mWatcher = new FileSystemWatcher(dir, "*.html")
            {
                IncludeSubdirectories = true,
                EnableRaisingEvents = true,
            };
            mWatcher.Changed += onChanged;
        }

        private void compile(string filePath)
        {
            var template = File.ReadAllText(filePath);
            string render = null;
            string staticRender = null;
            try
            {
                var result = mEnv.DoString(string.Format(@"
local template = [[{0}]]
local blaze = require 'blaze'
local compile = require('blaze.vue.platforms.unity.entry-runtime-with-compiler').compile
return compile(template)
", template));

                var table = (LuaTable) result[0];
                render = table.Get<string>("render");
                staticRender = table.Get<string>("staticRender");
            }
            catch (Exception e1)
            {
                Debug.LogWarning("编译异常：" + e1);
                return;
            }

            var targetFilePath = Path.ChangeExtension(filePath, ".lua");
            if (!File.Exists(targetFilePath))
                return;
            replace(targetFilePath, render, staticRender);
        }

        private void onChanged(object sender, FileSystemEventArgs e)
        {
            if (Path.GetExtension(e.FullPath) != ".html")
                return;
            if (!File.Exists(e.FullPath))
                return;
            Debug.Log("changed -> " + e.FullPath);
            lock (mFilePaths)
            {
                mFilePaths.Add(e.FullPath);
            }
        }

        private void onEditorApplicationUpdate()
        {
            List<string> files = null;
            lock (mFilePaths)
            {
                if (mFilePaths.Count == 0)
                    return;
                files = mFilePaths.ToList();
                mFilePaths.Clear();
            }

            foreach (var file in files)
                compile(file);
        }

        private void replace(string filePath, string render, string staticRender)
        {
            if (render == null && staticRender == null)
                return;
            var content = File.ReadAllText(filePath);
            var userContentEndPos = content.IndexOf(mAutoGeneratedBegin, StringComparison.Ordinal);
            if (userContentEndPos < 0)
            {
                userContentEndPos = content.LastIndexOf("return M", StringComparison.Ordinal);
                if (userContentEndPos < 0)
                    return;
            }

            var buffer = new StringBuilder();
            buffer.Append(content.Substring(0, userContentEndPos));
            buffer.AppendLine(mAutoGeneratedBegin);
            buffer.AppendLine();
            if (render != null)
                buffer.AppendFormat("M.render = function(self, h)\r\n    return blaze.with(self, function(_ENV)\r\n        {0}\r\n    end)\r\nend\r\n", render);
            buffer.AppendLine();
            if (staticRender != null)
                buffer.AppendFormat("M.static_render_fns = function(self, h)\r\n    return blaze.with(self, function(_ENV)\r\n        {0}\r\n    end)\r\nend\r\n", staticRender);
            buffer.AppendLine(mAutoGeneratedEnd);
            buffer.AppendLine();
            buffer.AppendLine("return M");
            File.WriteAllText(filePath, buffer.ToString());
        }

        ~AutoCompiler()
        {
            Dispose(false);
            EditorApplication.update -= onEditorApplicationUpdate;
        }

        private static string mAutoGeneratedBegin = "-- AUTO GENERATED BEGIN --";
        private static string mAutoGeneratedEnd = "-- AUTO GENERATED END --";

        private FileSystemWatcher mWatcher;
        private bool mIsDisposed;
        private LuaEnv mEnv = new LuaEnv();
        private readonly HashSet<string> mFilePaths = new HashSet<string>();
    }
}